package cn.calm.commons.service.defaultimpl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Embeddable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import cn.calm.commons.dao.QueryConditionBuilder;
import cn.calm.commons.entity.BaseEntity;

public class DefaultQueryConditionBuilder<T extends BaseEntity> implements
		QueryConditionBuilder<T> {
	protected T t;
	protected String asc;

	public DefaultQueryConditionBuilder(T t) {
		this(t, null);
	}

	public DefaultQueryConditionBuilder(T t, String asc) {
		this.t = t;
		this.asc = asc;
	}

	@Override
	public void query(CriteriaBuilder b, CriteriaQuery<?> queryq,
			Root<? extends BaseEntity> root) {
		Class<? extends BaseEntity> clazz = t.getClass();

		Field[] fields = clazz.getDeclaredFields();
		List<Predicate> predicates = new ArrayList<Predicate>();
		buildWherePredicate(t, b, root, fields, predicates);
		Predicate[] predicateTemp = new Predicate[predicates.size()];
		predicates.toArray(predicateTemp);
		queryq.where(predicateTemp);
		if (asc != null) {
			queryq.orderBy(b.asc(root.get(asc)));
		}
	}

	protected void buildWherePredicate(final T t, CriteriaBuilder r,
			Path<?> root, Field[] fields, List<Predicate> predicates) {
		Class<?> fieldType;
		for (Field f : fields) {
			if (isStaticOrFinal(f)) {
				fieldType = f.getType();
				f.setAccessible(true);
				build(t, r, root, predicates, fieldType, f);
			}
		}
	}

	protected void build(final T t, CriteriaBuilder r, Path<?> root,
			List<Predicate> predicates, Class<?> fieldType, Field f) {
		try {
			Object value = f.get(t);
			if (value != null) {
				if (fieldType.equals(String.class)) {
					if (!"".equals(value.toString())) {
						predicates.add(r.like(root.<String> get(f.getName()),
								"%" + value.toString() + "%"));
					}
				} else {
					Embeddable embeddable = fieldType
							.getAnnotation(Embeddable.class);
					if (embeddable != null) {
						Path<Serializable> path = root.<Serializable> get(f
								.getName());
						Field fields[] = fieldType.getDeclaredFields();
						for (Field fi : fields) {
							if (isStaticOrFinal(fi)) {
								build1(value, r, path, predicates,
										fi.getType(), fi);
							}
						}
					}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	protected void build1(Object t, CriteriaBuilder r, Path<Serializable> root,
			List<Predicate> predicates, Class<?> fieldType, Field f) {
		try {
			f.setAccessible(true);
			Object value = f.get(t);
			if (value != null) {
				if (fieldType.equals(String.class)) {
					if (!"".equals(value.toString())) {
						predicates.add(r.like(root.<String> get(f.getName()),
								"%" + value.toString() + "%"));
					}
				} else {
					Embeddable embeddable = fieldType
							.getAnnotation(Embeddable.class);
					if (embeddable != null) {
						Path<Serializable> path = root.<Serializable> get(f
								.getName());
						Field fields[] = fieldType.getDeclaredFields();
						for (Field fi : fields) {
							if (isStaticOrFinal(fi)) {
								build1(t, r, path, predicates, fi.getType(), fi);
							}
						}
					}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	protected boolean isStaticOrFinal(Field f) {
		return (f.getModifiers() & Modifier.FINAL) != Modifier.FINAL
				&& (f.getModifiers() & Modifier.STATIC) != Modifier.STATIC;
	}

	public String getAsc() {
		return asc;
	}

	public void setAsc(String asc) {
		this.asc = asc;
	}

}
